#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "../comm/Log.hpp"
#include "../comm/util.hpp"

namespace ns_compiler
{
    // 引入工具类
    using namespace ns_util;
    using namespace ns_log;

    class Compiler
    {
    public:
        Compiler() {}
        ~Compiler() {}

        // 将函数设置为静态函数,方便调用

        // 编译名为 file_name 的可执行文件
        static bool Compile(const std::string &file_name)
        {
            pid_t pid = fork();
            if (pid < 0)
            {
                lg(FATAL, "内部错误, fork error!\n");
                return false;
            }
            else if (pid == 0) // 子进程
            {
                // 我们希望如果编译失败,将报错信息打到一个文件里,而非stderr上
                umask(0); // 设置文件默认掩码
                // 创建报错信息文件
                int _stderr = open(PathUtil::CompilerError(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
                if (_stderr < 0)
                {
                    lg(WARNING, "编译报错信息临时文件生成失败\n");
                    exit(1);
                }
                // 可以使用重定向
                dup2(_stderr, 2); // stderr 的默认文件描述符为2

                // 执行程序替换->编译文件
                execlp("g++", "g++", "-o", PathUtil::Exe(file_name).c_str(), PathUtil::Src(file_name).c_str(),
                       "-D", "COMPILER_ONLINE", "-std=c++11", nullptr);
                lg(ERROR, "启动编译器g++失败，可能是参数错误\n");

                exit(2);
            }
            else // 父进程
            {
                waitpid(pid, nullptr, 0);
                // 检测进程是否替换成功,即检测是否成功生成了可执行文件
                if (FileUtil::IsFileExists(PathUtil::Exe(file_name)))
                {
                    lg(INFO, "compile success!\n");
                    return true;
                }
                else
                {
                    lg(FATAL, "compile fail\n");
                    return false;
                }
            }

            return false;
        }
    };
};